import pandas as pd
import numpy as np
from arch.univariate import arch_model
import warnings
from scipy.stats import norm
import numba
warnings.simplefilter("ignore")
# PARAMETRIC
@numba.jit(nopython=True)
def generate_scenarios(nbr_simulation):
random_scenarios = np.random.normal(0, 1, size=(nbr_simulation // 2))
antithetic_scenarios = -random_scenarios
return np.concatenate((random_scenarios, antithetic_scenarios))
@numba.jit(nopython=True)
def compute_std(data, ddof=1):
n = len(data)
mean = np.mean(data)
sum_squares = np.sum((data - mean) ** 2)
return np.sqrt(sum_squares / (n - ddof))
@numba.jit(nopython=True)
def compute_skewness(data):
n = len(data)
mean = np.mean(data)
std = compute_std(data, ddof=1)
skewness = np.sum(((data - mean) / std) ** 3)
return (n / ((n - 1) * (n - 2))) * skewness
@numba.jit(nopython=True)
def compute_kurtosis(data):
n = len(data)
mean = np.mean(data)
std = compute_std(data, ddof=1)
kurtosis = np.sum(((data - mean) / std) ** 4)
factor1 = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3))
factor2 = (3 * (n - 1) ** 2) / ((n - 2) * (n - 3))
return factor1 * kurtosis - factor2
class ParametricVaRCalculation:
def __init__(self, data, window, confidence_level):
"""
Initialize the VaR calculation class.
Parameters:
data (pd.Series or pd.DataFrame): Time series data of returns.
window (int): Rolling window size for VaR calculation.
confidence_level (float): Confidence level for VaR, between 0 and 1.
"""
if not isinstance(data, (pd.Series, pd.DataFrame)):
raise ValueError("Data must be a pandas Series or DataFrame.")
if not isinstance(window, int) or window <= 0:
raise ValueError("Window must be a positive integer.")
if not (isinstance(confidence_level, float) and 0 < confidence_level < 1):
raise ValueError("Confidence level must be a float between 0 and 1.")
if window > len(data):
raise ValueError("Window size cannot exceed the number of data points.")
self.data = data.replace([np.inf, -np.inf], np.nan).dropna()
self.window = window
self.confidence_level = confidence_level
def Variance_Covariance_VaR(self):
"""
This method calculates the Value at Risk (VaR) by assuming that returns are normally distributed.
VaR is computed using the mean (expected return), variance (as a measure of volatility), and a specified confidence interval.
The calculation provides a rolling VaR over a given period.
Returns:
- DataFrame: A pandas DataFrame containing the rolling VaR values for each observation in the time series beyond the initial specified window.
The index matches the dates of the input data series, allowing for easy comparison between the calculated VaR and actual returns.
Note:
- The input data (self.data) should represent returns and be prepared accordingly for accurate VaR calculation.
- The 'window' attribute of the class defines the size of the rolling window for calculating volatility.
- The 'confidence_level' attribute specifies the desired confidence level for VaR calculation (e.g., 0.95 for 95% confidence).
"""
n = len(self.data)
var_value = []
for i in range(self.window, n):
std = np.std(self.data[i-self.window:i], ddof=1)
var = std * norm.ppf(self.confidence_level)
var_value.append(-var)
return pd.DataFrame(var_value, index=self.data.index[self.window:])
def Parametric_CornishFisher_VaR(self):
"""
Calculates the rolling Value at Risk (VaR) using the Parametric Cornish-Fisher expansion.
This method accounts for the non-normality of returns by adjusting the VaR calculation for skewness and kurtosis observed in the windowed data.
The Cornish-Fisher expansion modifies the z-score from the normal distribution to incorporate the effects of skewness and kurtosis, providing a more accurate estimate of VaR for distributions that deviate from normality.
For each window of data, the function calculates:
- The standard deviation (as a measure of volatility),
- The skewness (to capture asymmetry of the distribution),
- The kurtosis (to capture tail heaviness or lightness of the distribution).
These statistics are then used to adjust the z-score from the normal distribution, which is subsequently used to calculate the VaR.
Returns:
- DataFrame: A pandas DataFrame containing the rolling VaR values for each observation in the time series beyond the initial specified window.
The index matches the dates of the input data series, enabling easy comparison between the calculated VaR and actual returns.
Note:
- The input data (self.data) should be in decimal format (e.g., 0.05 for a 5% return) for accurate VaR calculation.
- The 'window' attribute of the class defines the size of the rolling window for calculating standard deviation, skewness, and kurtosis.
- The 'confidence_level' attribute specifies the desired confidence level for VaR calculation (e.g., 0.95 for 95% confidence), which influences the z-score used in the adjustment.
"""
n = len(self.data)
var_value = []
for i in range(self.window, n):
window_data = self.data[i - self.window:i]
std = np.std(window_data, ddof=1)
skewness = compute_skewness(np.array(window_data))
kurt = compute_kurtosis(np.array(window_data))
z = norm.ppf(self.confidence_level)
z_adj = z + (z ** 2 - 1) * skewness / 6 + (z ** 3 - 3 * z) * (kurt - 3) / 24 - (2 * z ** 3 - 5 * z) * (skewness ** 2) / 36
var = std * - z_adj
var_value.append(var)
return pd.DataFrame(var_value, index=self.data.index[self.window:])
def Parametric_EWMA_var(self, lambda_: float=0.94):
"""
Calculates the rolling Value at Risk (VaR) using the Exponentially Weighted Moving Average (EWMA) model for volatility.
The EWMA model gives more weight to recent observations while exponentially decreasing the weight of older observations, providing a dynamic view of risk that adjusts more quickly to recent market changes compared to simple moving average methods.
Parameter:
- lambda_ (float): The decay factor for the EWMA model, controlling the rate at which weights decrease for older observations. lambda_ must be between 0 and 1, exclusive.
Raises:
- ValueError: If lambda_ is not in the interval (0, 1).
Returns:
- DataFrame: A pandas DataFrame containing the rolling VaR values for each observation in the time series, starting after the initial specified window.
The index aligns with the dates of the input data series, allowing for straightforward comparison between the calculated VaR and actual returns.
Note:
- The input data (self.data) should be in decimal format (e.g., 0.05 for a 5% return) for accurate VaR calculation.
- The 'window' attribute of the class defines the size of the rolling window for calculating squared returns, which are then used in the EWMA calculation.
- The 'confidence_level' attribute specifies the desired confidence level for VaR calculation (e.g., 0.95 for 95% confidence), influencing the z-score used to scale the EWMA standard deviation to a VaR value.
"""
if lambda_ <= 0 or lambda_ >= 1:
raise ValueError("lambda_ must be between 0 and 1.")
squared_returns = self.data ** 2
ewma_squared = squared_returns.ewm(alpha=(1 - lambda_), adjust=False).mean()
ewma_volatility = np.sqrt(ewma_squared)
z_score = norm.ppf(self.confidence_level)
ewma_var = -ewma_volatility * z_score
return ewma_var[self.window:]
def GARCH_VaR(self, forecasted_VaR=False, model='Garch', distribution='normal'):
"""
Calculates rolling Value at Risk (VaR) using GARCH-family models including GARCH, EGARCH, and TGARCH, depending on the specified model type. This function models the volatility clustering and leverage effects commonly observed in financial time series, making it highly effective for dynamic market conditions where traditional static models may fall short. The method leverages the conditional volatility estimated from the chosen GARCH-family model to compute the VaR at a specified confidence level, providing a nuanced view of risk that accounts for market changes over time.
Parameters:
- forecasted_VaR (bool): If True, the function forecasts VaR using the model's one-step-ahead volatility forecast. If False, it uses the last estimated conditional volatility for VaR calculation. Defaults to False, which relies on historical volatility.
- model (str): Specifies the type of GARCH model to use for volatility estimation. Acceptable values are 'GARCH' (default), 'EGARCH', and 'TGARCH'. Each model type offers different features: 'GARCH' for basic volatility clustering, 'EGARCH' for capturing asymmetric effects of shocks on volatility (leverage effect), and 'TGARCH' for modeling different responses of volatility to positive and negative shocks.
- distribution (str): Determines the distribution used for the standardized residuals in the GARCH model estimation. Options are 'normal' (default), 't' (Student's t-distribution), 'ged' (Generalized Error Distribution), and 'skewt' (Skewed Student's t-distribution). Choosing a distribution other than 'normal' allows for modeling of fat tails and skewness in return distributions.
Returns:
- DataFrame: A pandas DataFrame containing the rolling VaR estimates for each time point in the input data series beyond the initial window period specified. The DataFrame's index corresponds to the dates in the input data series, facilitating direct comparison between the calculated VaR and the actual returns. Each VaR estimate reflects the maximum expected loss at the given confidence level, based on the volatility dynamics captured by the selected GARCH-family model.
Note:
- The input data (self.data) should consist of return series in decimal format (e.g., 0.01 for 1%). It's crucial that the data is correctly formatted and cleaned of any outliers or errors to ensure accurate VaR calculation.
- Returns are scaled by 100 before GARCH model fitting to enhance numerical stability and aid convergence. Post-estimation, we revert the scaling to match the original returns' scale for accurate VaR calculations.
- The 'window' attribute specifies the rolling window size for the volatility model estimation, affecting how the model adapts to recent market information. A larger window offers more stability in volatility estimates but may react slower to recent market changes.
- The 'confidence_level' attribute sets the confidence level for the VaR calculation, typically chosen between 0.90 and 0.99. This parameter reflects the trade-off between the risk sensitivity of the VaR estimate and the frequency of expected VaR breaches.
Usage Example:
- To calculate the rolling VaR using an EGARCH model with Student's t-distribution for a series of daily returns, a window of 252 days, and a 95% confidence level, call the function as follows:
GARCH_VaR(forecasted_VaR=False, model='EGARCH', distribution='t')
Enhancements:
- This documentation provides clarity on the function's capabilities, parameters, and expected inputs and outputs, assisting users in effectively leveraging the function for advanced risk management tasks.
"""
if distribution.lower() not in ['skewt', 'ged', 't', 'normal']:
raise ValueError("The distribution parameter of the arch_model() function must be 'skewt', 'ged', 't', or 'normal'")
n = len(self.data)
var_values = np.zeros(n)
for i in range(self.window, n):
data_window = self.data[i-self.window:i]*100
if model.upper() == 'EGARCH':
res = arch_model(data_window, p=1, o=0, q=1, vol=model.upper(), dist=distribution.lower()).fit(disp='off')
elif model.upper() == 'TGARCH':
res = arch_model(data_window, p=1, o=1, q=1, vol=model.upper(), dist=distribution.lower()).fit(disp='off')
else:
res = arch_model(data_window, p=1, o=0, q=1, vol=model.upper(), dist=distribution.lower()).fit(disp='off')
if forecasted_VaR == True:
forecast = res.forecast(start=i - 1, horizon=1)
volatility = np.sqrt(forecast.variance.values[-1, :][0]/100)
else:
volatility = res.conditional_volatility[-1]/100
z_score = norm.ppf(self.confidence_level)
var_values[i] = -z_score * volatility
return pd.DataFrame(var_values[self.window:], index=self.data.index[self.window:], columns=['VaR'])
def Parametric_Monte_Carlo_Var(self, simulation: int=1000):
"""
Calculates rolling Value at Risk (VaR) using a Parametric Monte Carlo simulation method with antithetic variates for variance reduction.
This approach generates simulated profit and loss (PnL) scenarios based on the historical mean and standard deviation of returns within a specified window, allowing for the estimation of VaR over different periods.
The method involves:
- Generating a specified number of simulated returns for each window, based on the normal distribution with parameters (mean, standard deviation) derived from historical data.
- For each set of simulations, an equal number of antithetic variates are generated to reduce the variance of the simulation.
- Calculating the VaR as the quantile of the aggregated simulated returns (average of normal and antithetic simulations) corresponding to the specified confidence level.
Parameters:
- simulation (int): The number of simulated return paths to generate. The actual number of simulations will be twice this number, due to antithetic variates.
- confidence_level (float): The confidence level for the VaR calculation, typically set at 0.95 (95%).
Returns:
- DataFrame: A pandas DataFrame containing the rolling VaR estimates for each observation in the time series, starting after the initial specified window.
The index matches the dates of the input data series, facilitating easy comparison between the calculated VaR and actual returns.
Note:
- The input data (self.data) should represent returns and be prepared accordingly for accurate VaR calculation.
- The 'window' attribute of the class defines the size of the rolling window for calculating the mean and standard deviation of returns.
- The 'confidence_level' attribute specifies the desired confidence level for VaR calculation.
"""
n = len(self.data)
var_values = []
for i in range(self.window, n):
data_window = self.data[i-self.window:i]
std_ret = np.std(data_window, ddof=1).item()
mean_ret = np.mean(data_window).item()
scenarios = generate_scenarios(simulation)
pnl_scenarios = mean_ret + std_ret * scenarios
window_var = np.quantile(pnl_scenarios, 1 - self.confidence_level)
var_values.append(window_var)
return pd.DataFrame(var_values, index=self.data.index[self.window:], columns=['VAR'])
import pandas as pd
import numpy as np
import warnings
from sklearn.neighbors import KernelDensity
import statsmodels.api as sm
from statsmodels.regression.quantile_regression import QuantReg
from joblib import Parallel, delayed
import numba
warnings.simplefilter("ignore")
class NonParametricVaRCalculation:
def __init__(self, data, window, confidence_level):
"""
Initialize the VaR calculation class.
Parameters:
data (pd.Series or pd.DataFrame): Time series data of returns.
window (int): Rolling window size for VaR calculation.
confidence_level (float): Confidence level for VaR, between 0 and 1.
"""
if not isinstance(data, (pd.Series, pd.DataFrame)):
raise ValueError("Data must be a pandas Series or DataFrame.")
if not isinstance(window, int) or window <= 0:
raise ValueError("Window must be a positive integer.")
if not (isinstance(confidence_level, float) and 0 < confidence_level < 1):
raise ValueError("Confidence level must be a float between 0 and 1.")
if window > len(data):
raise ValueError("Window size cannot exceed the number of data points.")
self.data = data.replace([np.inf, -np.inf], np.nan).dropna()
self.window = window
self.confidence_level = confidence_level
def VaR_historic(self):
"""
Calculate rolling historical Value at Risk (VaR) using the historical timeseries returns.
This approach involves to consider the pastes returns to be most relevant to compute the Value et Risk.
Returns:
- DataFrame: A pandas DataFrame containing the rolling VaR values for each observation in the time series beyond the initial specified window.
The index corresponds to the dates of the input data series, facilitating easy comparison between the calculated VaR and the actual returns.
Note:
- The input data (self.data) should be in decimal format (e.g., 0.05 for a 5% return).
- The 'window' attribute of the class should be set to define the size of the rolling window for GARCH model fitting.
- The 'confidence_level' attribute should specify the desired confidence level for VaR calculation (e.g., 0.95 for 95% confidence).
"""
if len(self.data) < self.window:
raise ValueError("Length of data must be greater than the window size.")
n = len(self.data)
var_value = []
for i in range(self.window, n):
data_window = self.data[i - self.window:i]
var = np.quantile(data_window, 1 - self.confidence_level)
var_value.append(var)
return pd.DataFrame(var_value, index=self.data.index[self.window:])
def bootstrap_sample(self, data_window, nbr_simulation, confidence_level):
indices = np.random.randint(0, len(data_window), (nbr_simulation, len(data_window)))
samples = data_window.values[indices]
return np.quantile(samples, 1 - confidence_level, axis=1)
def VaR_bootstrap(self, nbr_simulation: int = 1000):
"""
Calculate rolling historical Value at Risk (VaR) using the Bootstrap resampling method on timeseries returns.
This approach involves to consider the pastes returns through a high nuber of simulation to compute the Value et Risk, by selecting a certain quantile of the simulated path.
Returns:
- DataFrame: A pandas DataFrame containing the rolling VaR values for each observation in the time series beyond the initial specified window.
The index corresponds to the dates of the input data series, facilitating easy comparison between the calculated VaR and the actual returns.
Note:
- The input data (self.data) should be in decimal format (e.g., 0.05 for a 5% return).
- The 'window' attribute of the class should be set to define the size of the rolling window for GARCH model fitting.
- The 'confidence_level' attribute should specify the desired confidence level for VaR calculation (e.g., 0.95 for 95% confidence).
"""
n = len(self.data)
var_values = []
results = Parallel(n_jobs=-1)(delayed(self.bootstrap_sample)
(self.data[i - self.window:i],
nbr_simulation, self.confidence_level)
for i in range(self.window, n)
)
for result in results:
var_values.append(np.median(result))
return pd.DataFrame(var_values, index=self.data.index[self.window:], columns=['VaR'])
def Quantile_Regression_VaR(self, predictor=None):
"""
Calculates rolling historical Value at Risk (VaR) using Quantile Regression, which estimates the specific quantile of time series returns corresponding to VaR. This method leverages past returns to model the conditional quantile that represents the VaR at a given confidence level directly.
Parameters:
- predictor (type: varies): The predictor variable(s) used in the quantile regression. If None, the model will use only a constant term as the predictor. This can be a pandas Series, DataFrame, or any structure compatible with statsmodels.
Returns:
- DataFrame: A pandas DataFrame containing the rolling VaR values for each observation in the time series, starting from the end of the initial specified window. The index matches the dates of the input data series, enabling an easy comparison between the calculated VaR and actual returns.
Note:
- The input data (self.data) should be in decimal format (e.g., 0.05 for a 5% return) to ensure accuracy in the calculation.
- The 'window' attribute of the class must be set to define the size of the rolling window over which the quantile regression is applied. This window moves through the dataset to calculate VaR at each step.
- The 'confidence_level' attribute should specify the desired confidence level for VaR calculation (e.g., 0.95 for 95% confidence), directly influencing the quantile used in the regression.
"""
n = len(self.data)
var_values = []
for i in range(self.window, n):
end_idx = i
start_idx = i - self.window
Y = self.data[start_idx:end_idx]
if predictor is None:
X = np.ones((len(Y), 1)) # Only use a constant term
else:
if isinstance(predictor, pd.Series):
X = predictor[start_idx:end_idx].values.reshape(-1, 1)
X = sm.add_constant(X) # Adding constant term to the predictor
elif isinstance(predictor, pd.DataFrame):
X = predictor[start_idx:end_idx]
X = sm.add_constant(X) # Ensure constant term is included
else:
raise ValueError("Predictor must be a pandas Series or DataFrame")
model = QuantReg(Y, X).fit(q=self.confidence_level)
if X.shape[1] == 1: # Only constant term, no additional predictors
VaR_estimate = -(model.params[0])
else:
VaR_estimate = -(model.params[0] + np.dot(model.params[1:], X[-1, 1:]))
var_values.append(VaR_estimate)
return pd.DataFrame(var_values, index=self.data.index[self.window:])
def VaR_kernel(self, bandwidth_rule: str='Scott'):
"""
Calculates rolling Value at Risk (VaR) using kernel density estimation (KDE). The KDE is defined as:
f(x) = (1 / (n * h)) * ∑ K((x - Xi) / h)
where: f(x) is the estimated density at point x, n is the number of data points, Xi are the data points,
- K(⋅) represents the kernel function, a symmetric function that integrates to one. Common choices include Gaussian and Epanechnikov kernels,
- h denotes the bandwidth, a parameter controlling the smoothness of the resulting density curve.
Bandwidth determination methods:
- 'Scott' rule: h = 1.06 * std(x) * n^(-1/5),
- 'Silverman' rule: h = 0.9 * min(std(x), IQR / 1.35) * n^(-1/5),
- Cross-validation: Minimizes prediction error among all selections.
The method computes the cumulative density function (CDF) of f(x) and derives its inverse F^-1 for a specified confidence level.
Parameters:
- nbr_simulation (int): The number of return paths simulated from the KDE.
- bandwidth_rule (str): Specifies the rule for determining the bandwidth ('Scott' or 'Silverman').
Returns:
- DataFrame: A pandas DataFrame containing rolling VaR estimates for each observation in the time series, starting after the initial window. The index aligns with the dates of the input data series, enabling easy comparison between calculated VaR and actual returns.
Notes:
- The input data (self.data) should be in decimal format (e.g., 0.05 for a 5% return) for accurate calculation.
- The 'window' attribute of the class defines the size of the rolling window for model fitting.
- The 'confidence_level' attribute specifies the desired confidence level for VaR calculation (e.g., 0.95 for 95% confidence).
"""
n = len(self.data)
var_values = []
for i in range(self.window, n):
data_window = self.data[i - self.window:i].to_numpy().flatten()
if bandwidth_rule.lower() == 'scott':
bandwidth = 1.06 * np.std(data_window) * len(data_window) ** (-1 / 5)
else:
IQR = np.subtract(*np.percentile(data_window, [75, 25]))
sigma_hat = np.std(data_window, ddof=1)
bandwidth = 0.9 * min(sigma_hat, IQR / 1.35) * len(data_window) ** (-1 / 5)
kde = KernelDensity(bandwidth=bandwidth, kernel='gaussian')
kde.fit(data_window[:, np.newaxis])
grid = np.linspace(data_window.min(), data_window.max(), 1000)
grid_reshaped = grid[:, np.newaxis]
kde_values = np.exp(kde.score_samples(grid_reshaped))
cdf = np.cumsum(kde_values * np.diff(grid, prepend=[grid[0]]))
cdf /= cdf[-1]
VaR_threshold = 1 - self.confidence_level
VaR = grid[np.searchsorted(cdf, VaR_threshold)]
var_values.append(VaR)
return pd.DataFrame(var_values, index=self.data.index[self.window:])
import pandas as pd
import numpy as np
import yfinance as yf
from arch.univariate import arch_model
import warnings
from scipy.stats import norm
import plotly.graph_objects as go
from VaR_Parametric import ParametricVaRCalculation
from VaR_NonParametric import NonParametricVaRCalculation
from IPython.display import display, Markdown
warnings.simplefilter("ignore")
Portfolio_1 = pd.DataFrame(yf.download(["^VIX", "AAPL", "MSFT", "^GSPC"], "1990-01-01").Close)
PnL_Portfolio_1 = pd.DataFrame(np.sum(Portfolio_1.pct_change().dropna()*1/len(Portfolio_1.columns), axis=1), columns=['PnL_portfolio'])
print(PnL_Portfolio_1)
predictor_1 = Portfolio_1["^GSPC"].pct_change().dropna()
[*********************100%***********************] 4 of 4 completed
PnL_portfolio
Date
1990-01-03 0.016216
1990-01-04 0.020190
1990-01-05 0.003846
1990-01-08 0.008484
1990-01-09 0.017838
... ...
2024-06-03 0.005301
2024-06-04 0.003283
2024-06-05 -0.000380
2024-06-06 -0.002513
2024-06-07 -0.004730
[8674 rows x 1 columns]
display(Markdown("""
## Variance-Covariance VaR
The Variance-Covariance VaR method, also known as the Parametric VaR, assumes that returns of financial assets are normally distributed. It calculates the VaR by quantifying the maximum loss at a specific confidence level based on the standard deviation (volatility) and mean (expected return) of asset returns.
### Formula
The VaR for a normally distributed return is calculated using the formula:
$$
\\text{VaR}_{\\alpha} = -\\left(\\mu + z_{\\alpha} \\cdot \\sigma\\right)
$$
Where:
- $\\mu$ is the mean (or expected return) of the asset returns.
- $\\sigma$ is the standard deviation of the asset returns, representing the risk or volatility.
- $z_{\\alpha}$ is the z-score corresponding to the desired confidence level $\\alpha$ from the standard normal distribution. For example, for a 95% confidence level, $z_{\\alpha}$ is approximately 1.645.
### Interpretation
This method provides a straightforward calculation of VaR assuming normal distribution of returns. It effectively captures the risk associated with average market conditions but may not fully account for extreme market events or anomalies in the return distribution. The calculation focuses on the center of the distribution and adjusts for the tails using the z-score, which scales the standard deviation.
"""))
rolling_VaR_95 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.95).Variance_Covariance_VaR()
rolling_VaR_99 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.99).Variance_Covariance_VaR()
fig = go.Figure()
fig.add_trace(go.Scatter(x=PnL_Portfolio_1.index, y=PnL_Portfolio_1['PnL_portfolio'], name='PnL Portfolio'))
fig.add_trace(go.Scatter(x=rolling_VaR_95.index, y=rolling_VaR_95.squeeze(), name='Rolling VaR 95%'))
fig.add_trace(go.Scatter(x=rolling_VaR_99.index, y=rolling_VaR_99.squeeze(), name='Rolling VaR 99%'))
fig.update_layout(title='Portfolio PnL and Rolling VaR', xaxis_title='Date', yaxis_title='Value')
fig.show()
The Variance-Covariance VaR method, also known as the Parametric VaR, assumes that returns of financial assets are normally distributed. It calculates the VaR by quantifying the maximum loss at a specific confidence level based on the standard deviation (volatility) and mean (expected return) of asset returns.
The VaR for a normally distributed return is calculated using the formula:
$$ \text{VaR}_{\alpha} = -\left(\mu + z_{\alpha} \cdot \sigma\right) $$Where:
This method provides a straightforward calculation of VaR assuming normal distribution of returns. It effectively captures the risk associated with average market conditions but may not fully account for extreme market events or anomalies in the return distribution. The calculation focuses on the center of the distribution and adjusts for the tails using the z-score, which scales the standard deviation.
display(Markdown("""
## Parametric Cornish-Fisher VaR
The Parametric Cornish-Fisher VaR expands the traditional Variance-Covariance approach by adjusting for skewness and kurtosis in the return distribution. This adjustment provides a more accurate estimation of VaR under non-normal distributions by correcting the z-score used in the VaR calculation to reflect the actual distribution shape.
### Formula
The Cornish-Fisher expansion modifies the z-score to include terms for skewness and kurtosis:
$$
z_{\\text{CF}} = z_{\\alpha} + \\frac{1}{6} (z_{\\alpha}^2 - 1) S + \\frac{1}{24} (z_{\\alpha}^3 - 3z_{\\alpha}) (K - 3) - \\frac{1}{36} (2z_{\\alpha}^3 - 5z_{\\alpha}) S^2
$$
Where:
- $z_{\\alpha}$ is the z-score from the standard normal distribution corresponding to the confidence level $\\alpha$.
- $S$ is the skewness of the return distribution, measuring asymmetry.
- $K$ is the excess kurtosis of the return distribution, measuring tail weight compared to a normal distribution.
The VaR is then calculated using this adjusted z-score:
$$
\\text{VaR}_{\\alpha} = -\\left(\\mu + z_{\\text{CF}} \\cdot \\sigma\\right)
$$
Where:
- $\\mu$ is the mean (or expected return) of the asset returns.
- $\\sigma$ is the standard deviation of the asset returns.
### Interpretation
The Cornish-Fisher expansion allows the VaR to reflect more accurately the potential risk in scenarios where the return distribution is not perfectly normal. This is particularly useful in financial markets where asymmetry and heavy tails are common. The skewness adjustment accounts for the direction of the distribution's asymmetry, while the kurtosis adjustment focuses on the likelihood of extreme outcomes, refining the risk measurement by adjusting for these real-world deviations from normality.
"""))
rolling_VaR_95 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.95).Parametric_CornishFisher_VaR()
rolling_VaR_99 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.99).Parametric_CornishFisher_VaR()
fig = go.Figure()
fig.add_trace(go.Scatter(x=PnL_Portfolio_1.index, y=PnL_Portfolio_1['PnL_portfolio'], name='PnL Portfolio'))
fig.add_trace(go.Scatter(x=rolling_VaR_95.index, y=rolling_VaR_95.squeeze(), name='Rolling VaR 95%'))
fig.add_trace(go.Scatter(x=rolling_VaR_99.index, y=rolling_VaR_99.squeeze(), name='Rolling VaR 99%'))
fig.update_layout(title='Portfolio PnL and Rolling VaR', xaxis_title='Date', yaxis_title='Value')
fig.show()
The Parametric Cornish-Fisher VaR expands the traditional Variance-Covariance approach by adjusting for skewness and kurtosis in the return distribution. This adjustment provides a more accurate estimation of VaR under non-normal distributions by correcting the z-score used in the VaR calculation to reflect the actual distribution shape.
The Cornish-Fisher expansion modifies the z-score to include terms for skewness and kurtosis:
$$ z_{\text{CF}} = z_{\alpha} + \frac{1}{6} (z_{\alpha}^2 - 1) S + \frac{1}{24} (z_{\alpha}^3 - 3z_{\alpha}) (K - 3) - \frac{1}{36} (2z_{\alpha}^3 - 5z_{\alpha}) S^2 $$Where:
The VaR is then calculated using this adjusted z-score:
$$ \text{VaR}_{\alpha} = -\left(\mu + z_{\text{CF}} \cdot \sigma\right) $$Where:
The Cornish-Fisher expansion allows the VaR to reflect more accurately the potential risk in scenarios where the return distribution is not perfectly normal. This is particularly useful in financial markets where asymmetry and heavy tails are common. The skewness adjustment accounts for the direction of the distribution's asymmetry, while the kurtosis adjustment focuses on the likelihood of extreme outcomes, refining the risk measurement by adjusting for these real-world deviations from normality.
display(Markdown('''
## Parametric EWMA VaR
The Parametric Exponentially Weighted Moving Average (EWMA) VaR model is used to estimate potential future losses by giving more importance to recent returns. This approach adjusts for changing market volatility and is particularly useful in financial environments where volatility clustering occurs.
### Formula
The EWMA model computes the volatility using a smoothing constant $\lambda$, which dictates the decay factor for the exponential weighting:
$$
\\sigma^2_{t} = \\lambda \\sigma^2_{t-1} + (1-\\lambda) r_{t-1}^2
$$
Where:
- $\\sigma^2_{t}$ is the estimated variance on day $t$.
- $\\lambda$ is the decay factor, typically set close to 1 (e.g., 0.94).
- $r_{t-1}$ is the asset return at time $t-1$.
Given the calculated volatility, the VaR is computed as:
$$
\\text{VaR}_{\\alpha} = -\\left(z_{\\alpha} \\cdot \\sigma_t\\right)
$$
Where:
- $z_{\\alpha}$ is the z-score from the standard normal distribution for the desired confidence level $\alpha$ (e.g., 95% or 99%).
- $\\sigma_t$ is the standard deviation (the square root of $\\sigma^2_{t}$).
### Interpretation
The EWMA model is preferred over simple historical volatility models because it captures the effects of volatility clustering—large changes tend to be followed by large changes, of either sign, and small changes tend to be followed by small changes. The model's reliance on the smoothing constant $\lambda$ allows for a flexible adjustment of how much weight is placed on recent volatility, thereby adapting more quickly to market changes than equally weighted moving averages. This method is particularly valuable in assessing the risk of financial portfolios in the presence of changing market conditions.
'''))
rolling_VaR_95 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.95).Parametric_EWMA_var() #good
rolling_VaR_99 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.99).Parametric_EWMA_var() #good
fig = go.Figure()
fig.add_trace(go.Scatter(x=PnL_Portfolio_1.index, y=PnL_Portfolio_1['PnL_portfolio'], name='PnL Portfolio'))
fig.add_trace(go.Scatter(x=rolling_VaR_95.index, y=rolling_VaR_95.squeeze(), name='Rolling VaR 95%')) # Use squeeze() to convert DataFrame to Series if necessary
fig.add_trace(go.Scatter(x=rolling_VaR_99.index, y=rolling_VaR_99.squeeze(), name='Rolling VaR 99%')) # Use squeeze() to convert DataFrame to Series if necessary
fig.update_layout(title='Portfolio PnL and Rolling VaR', xaxis_title='Date', yaxis_title='Value')
fig.show()
The Parametric Exponentially Weighted Moving Average (EWMA) VaR model is used to estimate potential future losses by giving more importance to recent returns. This approach adjusts for changing market volatility and is particularly useful in financial environments where volatility clustering occurs.
The EWMA model computes the volatility using a smoothing constant $\lambda$, which dictates the decay factor for the exponential weighting:
$$ \sigma^2_{t} = \lambda \sigma^2_{t-1} + (1-\lambda) r_{t-1}^2 $$Where:
Given the calculated volatility, the VaR is computed as:
$$ \text{VaR}_{\alpha} = -\left(z_{\alpha} \cdot \sigma_t\right) $$Where:
The EWMA model is preferred over simple historical volatility models because it captures the effects of volatility clustering—large changes tend to be followed by large changes, of either sign, and small changes tend to be followed by small changes. The model's reliance on the smoothing constant $\lambda$ allows for a flexible adjustment of how much weight is placed on recent volatility, thereby adapting more quickly to market changes than equally weighted moving averages. This method is particularly valuable in assessing the risk of financial portfolios in the presence of changing market conditions.
display(Markdown('''
## Parametric GARCH VaR
The Parametric GARCH VaR utilizes the GARCH model to forecast future volatility, providing a dynamic measure of market risk that accounts for volatility clustering—a common characteristic in financial time series. This approach estimates the conditional volatility, which is then used to calculate VaR, reflecting more accurately the potential extreme losses in markets where volatility changes over time.
### Formula
1. **GARCH Model Specification**:
The GARCH(p, q) model is defined as follows:
$$
\\sigma_t^2 = \\omega + \\sum_{i=1}^p \\alpha_i \\epsilon_{t-i}^2 + \\sum_{j=1}^q \\beta_j \\sigma_{t-j}^2
$$
Where:
- $\\sigma_t^2$ is the conditional variance at time $t$.
- $\\omega$, $\\alpha_i$, and $\\beta_j$ are parameters of the model.
- $\\epsilon_t$ represents the residuals or returns at time $t$, assumed to be $i.i.d$.
- $p$ and $q$ are the orders of the GARCH and ARCH components respectively.
2. **VaR Calculation**:
The VaR is calculated using the forecasted volatility from the GARCH model:
$$
\\text{VaR}_{\\alpha} = -\\left(\\mu + z_{\\alpha} \\cdot \\sigma_t\\right)
$$
Where:
- $\\mu$ is the mean (or expected return) of the asset returns, typically assumed to be zero in financial models.
- $z_{\\alpha}$ is the z-score from the standard normal distribution corresponding to the confidence level $\\alpha$.
- $\\sigma_t$ is the standard deviation (square root of variance) forecasted by the GARCH model.
### Interpretation
The use of GARCH models in VaR calculation allows for adjustments based on the latest market conditions, capturing the "clustering" of volatility which can significantly affect the risk estimates in periods of market turmoil or calm. This method is highly beneficial in a financial context where understanding and predicting volatility dynamics can drastically influence risk management decisions and financial planning.
'''))
rolling_VaR_95 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.95).GARCH_VaR()
rolling_VaR_99 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.99).GARCH_VaR()
fig = go.Figure()
fig.add_trace(go.Scatter(x=PnL_Portfolio_1.index, y=PnL_Portfolio_1['PnL_portfolio'], name='PnL Portfolio'))
fig.add_trace(go.Scatter(x=rolling_VaR_95.index, y=rolling_VaR_95.squeeze(), name='Rolling VaR 95%'))
fig.add_trace(go.Scatter(x=rolling_VaR_99.index, y=rolling_VaR_99.squeeze(), name='Rolling VaR 99%'))
fig.update_layout(title='Portfolio PnL and Rolling VaR', xaxis_title='Date', yaxis_title='Value')
fig.show()
The Parametric GARCH VaR utilizes the GARCH model to forecast future volatility, providing a dynamic measure of market risk that accounts for volatility clustering—a common characteristic in financial time series. This approach estimates the conditional volatility, which is then used to calculate VaR, reflecting more accurately the potential extreme losses in markets where volatility changes over time.
GARCH Model Specification: The GARCH(p, q) model is defined as follows: $$ \sigma_t^2 = \omega + \sum_{i=1}^p \alpha_i \epsilon_{t-i}^2 + \sum_{j=1}^q \beta_j \sigma_{t-j}^2 $$ Where:
VaR Calculation: The VaR is calculated using the forecasted volatility from the GARCH model: $$ \text{VaR}_{\alpha} = -\left(\mu + z_{\alpha} \cdot \sigma_t\right) $$ Where:
The use of GARCH models in VaR calculation allows for adjustments based on the latest market conditions, capturing the "clustering" of volatility which can significantly affect the risk estimates in periods of market turmoil or calm. This method is highly beneficial in a financial context where understanding and predicting volatility dynamics can drastically influence risk management decisions and financial planning.
display(Markdown("""
## Parametric Monte Carlo VaR
The Parametric Monte Carlo VaR uses simulations to forecast potential future losses over a specified time horizon under varying market conditions. This method relies on the generation of a large number of simulated price paths for the assets based on their historical volatility and correlations, calculating the potential returns for each simulation, and then determining the VaR from these simulated returns.
### Formula
1. **Simulate Random Returns**: Returns are simulated using the asset's historical mean and standard deviation, often adjusted for drift and other factors:
$$
r_t = \\mu \\Delta t + \\sigma \\epsilon_t \\sqrt{\\Delta t}
$$
Where:
- $r_t$ is the return at time $t$.
- $\\mu$ is the mean return.
- $\\sigma$ is the standard deviation of returns.
- $\\Delta t$ is the time increment.
- $\\epsilon_t$ is a random draw from a standard normal distribution.
2. **Aggregate Simulated Returns**: For each path, aggregate returns over the time horizon to simulate final asset prices.
3. **Compute VaR**: Calculate the percentile of the resulting distribution of simulated returns at the confidence level, typically 95% or 99%.
### Steps
- **Step 1: Generate Simulations**: Use the asset’s historical return data to simulate a large number of potential price paths over the desired horizon (e.g., 1000 simulations).
- **Step 2: Calculate Endpoint Prices**: For each simulated path, calculate the endpoint asset price based on the simulated returns.
- **Step 3: Determine VaR**: Sort the resulting set of endpoint prices or returns and pick the value at the $(1 - \\alpha)$ percentile, where $\\alpha$ is the confidence level.
### Interpretation
Monte Carlo VaR is particularly useful for measuring risk in complex portfolios where the relationships between asset returns are not straightforward or where the returns are not normally distributed. This method is flexible in its ability to model various types of financial instruments and market scenarios, making it a powerful tool for risk assessment in dynamic financial markets.
"""))
rolling_VaR_95 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.95).Parametric_Monte_Carlo_Var()
rolling_VaR_99 = ParametricVaRCalculation(PnL_Portfolio_1, 63, 0.99).Parametric_Monte_Carlo_Var()
fig = go.Figure()
fig.add_trace(go.Scatter(x=PnL_Portfolio_1.index, y=PnL_Portfolio_1['PnL_portfolio'], name='PnL Portfolio'))
fig.add_trace(go.Scatter(x=rolling_VaR_95.index, y=rolling_VaR_95.squeeze(), name='Rolling VaR 95%'))
fig.add_trace(go.Scatter(x=rolling_VaR_99.index, y=rolling_VaR_99.squeeze(), name='Rolling VaR 99%'))
fig.update_layout(title='Portfolio PnL and Rolling VaR', xaxis_title='Date', yaxis_title='Value')
fig.show()
The Parametric Monte Carlo VaR uses simulations to forecast potential future losses over a specified time horizon under varying market conditions. This method relies on the generation of a large number of simulated price paths for the assets based on their historical volatility and correlations, calculating the potential returns for each simulation, and then determining the VaR from these simulated returns.
Simulate Random Returns: Returns are simulated using the asset's historical mean and standard deviation, often adjusted for drift and other factors:
$$ r_t = \mu \Delta t + \sigma \epsilon_t \sqrt{\Delta t} $$
Where:
Aggregate Simulated Returns: For each path, aggregate returns over the time horizon to simulate final asset prices.
Compute VaR: Calculate the percentile of the resulting distribution of simulated returns at the confidence level, typically 95% or 99%.
Monte Carlo VaR is particularly useful for measuring risk in complex portfolios where the relationships between asset returns are not straightforward or where the returns are not normally distributed. This method is flexible in its ability to model various types of financial instruments and market scenarios, making it a powerful tool for risk assessment in dynamic financial markets.
display(Markdown("""
## Historical Simulation VaR
Historical Simulation VaR is a non-parametric approach that calculates potential future losses of an investment based on actual historical returns. This method assumes that past market movements will occur again in the future. The VaR is determined by taking the nth worst loss from the sorted dataset of historical returns, where n corresponds to the confidence level. For instance, for a 95% confidence level in a dataset of 1000 returns, the 950th worst loss would be considered the VaR.
### Formula
$$
\\text{VaR}_{\\alpha} = -\\inf \\left\\{ x \\in \\mathbb{R} : F(x) \\geq 1-\\alpha \\right\\}
$$
Where:
- $F(x)$ is the empirical cumulative distribution function of historical loss data.
- $\\alpha$ is the confidence level (typically 95% or 99%).
"""))
rolling_VaR_95 = NonParametricVaRCalculation(PnL_Portfolio_1, 63, 0.95).VaR_historic()
rolling_VaR_99 = NonParametricVaRCalculation(PnL_Portfolio_1, 63, 0.99).VaR_historic()
fig = go.Figure()
fig.add_trace(go.Scatter(x=PnL_Portfolio_1.index, y=PnL_Portfolio_1['PnL_portfolio'], name='PnL Portfolio'))
fig.add_trace(go.Scatter(x=rolling_VaR_95.index, y=rolling_VaR_95.squeeze(), name='Rolling VaR 95%'))
fig.add_trace(go.Scatter(x=rolling_VaR_99.index, y=rolling_VaR_99.squeeze(), name='Rolling VaR 99%'))
fig.update_layout(title='Portfolio PnL and Rolling VaR', xaxis_title='Date', yaxis_title='Value')
fig.show()
Historical Simulation VaR is a non-parametric approach that calculates potential future losses of an investment based on actual historical returns. This method assumes that past market movements will occur again in the future. The VaR is determined by taking the nth worst loss from the sorted dataset of historical returns, where n corresponds to the confidence level. For instance, for a 95% confidence level in a dataset of 1000 returns, the 950th worst loss would be considered the VaR.
Where:
display(Markdown('''
## Non-Parametric Bootstrap VaR
Non-Parametric Bootstrap VaR is a resampling method used to estimate the Value at Risk (VaR) by simulating the distribution of returns based on historical data. This method does not rely on the assumption of normal distribution and is particularly useful when dealing with non-normal or undefined distributions.
### Formula
The Bootstrap VaR is calculated by repeatedly sampling with replacement from the historical return series to create a large number of simulated return paths. The VaR is then determined by the percentile corresponding to the confidence level in these simulated paths:
$$
\\text{VaR}_{\\alpha} = -\\text{quantile}(R^*, 1-\\alpha)
$$
Where:
- $R^*$ represents the bootstrap resampled returns.
- $\\alpha$ is the confidence level (typically 95% or 99%).
### Steps
1. **Resample**: Draw a large number of samples with replacement from the historical returns to form resampled return series.
2. **Calculate Returns**: Compute the returns or losses for each resampled series.
3. **Quantile Calculation**: Determine the $(1-\\alpha)$th quantile of the resampled returns. This quantile represents the VaR at the desired confidence level.
### Interpretation
The bootstrap method's strength lies in its flexibility and non-reliance on the distributional assumptions of the return data. By resampling from the actual historical data, it captures the empirical characteristics of the return distribution, including skewness, kurtosis, and serial correlation. This approach is robust to model specification errors that can affect parametric methods, providing a more reliable and realistic measure of potential future losses.
'''))
rolling_VaR_95 = NonParametricVaRCalculation(PnL_Portfolio_1, 63, 0.95).VaR_bootstrap()
rolling_VaR_99 = NonParametricVaRCalculation(PnL_Portfolio_1, 63, 0.99).VaR_bootstrap()
fig = go.Figure()
fig.add_trace(go.Scatter(x=PnL_Portfolio_1.index, y=PnL_Portfolio_1['PnL_portfolio'], name='PnL Portfolio'))
fig.add_trace(go.Scatter(x=rolling_VaR_95.index, y=rolling_VaR_95.squeeze(), name='Rolling VaR 95%'))
fig.add_trace(go.Scatter(x=rolling_VaR_99.index, y=rolling_VaR_99.squeeze(), name='Rolling VaR 99%'))
fig.update_layout(title='Portfolio PnL and Rolling VaR', xaxis_title='Date', yaxis_title='Value')
fig.show()
Non-Parametric Bootstrap VaR is a resampling method used to estimate the Value at Risk (VaR) by simulating the distribution of returns based on historical data. This method does not rely on the assumption of normal distribution and is particularly useful when dealing with non-normal or undefined distributions.
The Bootstrap VaR is calculated by repeatedly sampling with replacement from the historical return series to create a large number of simulated return paths. The VaR is then determined by the percentile corresponding to the confidence level in these simulated paths:
$$ \text{VaR}_{\alpha} = -\text{quantile}(R^*, 1-\alpha) $$Where:
The bootstrap method's strength lies in its flexibility and non-reliance on the distributional assumptions of the return data. By resampling from the actual historical data, it captures the empirical characteristics of the return distribution, including skewness, kurtosis, and serial correlation. This approach is robust to model specification errors that can affect parametric methods, providing a more reliable and realistic measure of potential future losses.
display(Markdown('''
## Non-Parametric Quantile Regression VaR
Quantile Regression VaR is a non-parametric approach that estimates the potential future losses of an investment by modeling the conditional quantile of returns. Unlike parametric models, which assume a normal distribution of returns, quantile regression does not make such assumptions, allowing for a more flexible and robust estimation of risk levels across different probabilities.
### Formula
Quantile regression for VaR is formulated as follows:
$$
\\text{VaR}_{\\alpha}(X) = Q_{\\alpha}(Y | X = x)
$$
Where:
- $Q_{\\alpha}$ is the quantile function, estimating the $\alpha$-th quantile of the conditional distribution of $Y$ given $X = x$.
- $Y$ represents the returns.
- $X$ represents the predictors or independent variables; if $X$ is not included, the model estimates the unconditional quantile.
The regression equation is specified as:
$$
Q_{\\alpha}(Y | X) = X \\beta_{\\alpha}
$$
Where:
- $\\beta_{\\alpha}$ are the parameters estimated from the quantile regression, specific to the quantile $\alpha$.
### Estimation
The coefficients $\\beta_{\\alpha}$ are estimated by solving the following minimization problem:
$$
\\min_{\\beta_{\\alpha}} \\sum_{i=1}^{n} \\rho_{\\alpha}(y_i - x_i^T \\beta_{\\alpha})
$$
Where:
- $\\rho_{\\alpha}(u) = u(\\alpha - I(u < 0))$ is the check function, which weighs residuals differently depending on whether they are above or below the quantile being estimated, effectively focusing the fit on the $\alpha$ quantile of the distribution.
### Interpretation
By directly modeling the $\alpha$-th quantile of the return distribution, Quantile Regression VaR can adapt to the asymmetries and tail behaviors inherent in financial return distributions. This method is particularly useful for risk management purposes as it provides a direct estimation of the threshold below which $\alpha$ percent of returns may fall, considering the current or historical covariates.
'''))
rolling_VaR_95 = NonParametricVaRCalculation(PnL_Portfolio_1, 63, 0.95).Quantile_Regression_VaR(predictor_1) #good
rolling_VaR_99 = NonParametricVaRCalculation(PnL_Portfolio_1, 63, 0.99).Quantile_Regression_VaR(predictor_1) #good
fig = go.Figure()
fig.add_trace(go.Scatter(x=PnL_Portfolio_1.index, y=PnL_Portfolio_1['PnL_portfolio'], name='PnL Portfolio'))
fig.add_trace(go.Scatter(x=rolling_VaR_95.index, y=rolling_VaR_95.squeeze(), name='Rolling VaR 95%')) # Use squeeze() to convert DataFrame to Series if necessary
fig.add_trace(go.Scatter(x=rolling_VaR_99.index, y=rolling_VaR_99.squeeze(), name='Rolling VaR 99%')) # Use squeeze() to convert DataFrame to Series if necessary
fig.update_layout(title='Portfolio PnL and Rolling VaR', xaxis_title='Date', yaxis_title='Value')
fig.show()
Quantile Regression VaR is a non-parametric approach that estimates the potential future losses of an investment by modeling the conditional quantile of returns. Unlike parametric models, which assume a normal distribution of returns, quantile regression does not make such assumptions, allowing for a more flexible and robust estimation of risk levels across different probabilities.
Quantile regression for VaR is formulated as follows:
$$ \text{VaR}_{\alpha}(X) = Q_{\alpha}(Y | X = x) $$Where:
The regression equation is specified as:
$$ Q_{\alpha}(Y | X) = X \beta_{\alpha} $$Where:
The coefficients $\beta_{\alpha}$ are estimated by solving the following minimization problem:
$$ \min_{\beta_{\alpha}} \sum_{i=1}^{n} \rho_{\alpha}(y_i - x_i^T \beta_{\alpha}) $$Where:
By directly modeling the $lpha$-th quantile of the return distribution, Quantile Regression VaR can adapt to the asymmetries and tail behaviors inherent in financial return distributions. This method is particularly useful for risk management purposes as it provides a direct estimation of the threshold below which $lpha$ percent of returns may fall, considering the current or historical covariates.
display(Markdown('''
## Non-Parametric Kernel VaR
The Non-Parametric Kernel VaR uses kernel density estimation (KDE) to approximate the probability distribution function of asset returns. KDE is a fundamental data smoothing problem where inferences about the population are made based on a finite data sample without assuming a parametric form for the distribution.
### Formula and Methodology
Kernel density estimation is formulated as:
$$
\\hat{f}(x) = \\frac{1}{n h} \\sum_{i=1}^n K\\left(\\frac{x - X_i}{h}\\right)
$$
Where:
- $x$ is the point at which the density is estimated.
- $X_i$ are the sample points.
- $n$ is the number of sample points.
- $K(\\cdot)$ is the kernel function, a non-negative function that integrates to one and symmetrical around zero. Common choices are Gaussian, Epanechnikov, and uniform kernels.
- $h$ is the bandwidth, a scaling factor that affects the smoothness of the resulting density function.
The bandwidth can be chosen using rules like:
- **Scott's Rule**: $h = 1.06 \\sigma n^{-1/5}$
- **Silverman's Rule**: $h = 0.9 \\times \\min(\\sigma, \\frac{\\text{IQR}}{1.35}) \\times n^{-1/5}$
The VaR is determined by integrating the estimated density to find the cumulative distribution function (CDF), and then solving for the value at the desired confidence level $\alpha$:
$$
\\text{VaR}_{\\alpha} = F^{-1}(1 - \\alpha)
$$
Where $F^{-1}$ is the inverse of the estimated CDF.
### Interpretation
The Kernel VaR method provides a flexible approach to estimating VaR by making minimal assumptions about the underlying statistical distribution of returns. This flexibility makes it especially useful in financial contexts where the return distributions may not follow standard parametric models and may exhibit properties like skewness, kurtosis, or multimodality. The choice of bandwidth and kernel can significantly affect the estimation, and these should be selected based on the context and characteristics of the data.
'''))
rolling_VaR_95 = NonParametricVaRCalculation(PnL_Portfolio_1, 63, 0.95).VaR_kernel() #good 2nd check
rolling_VaR_99 = NonParametricVaRCalculation(PnL_Portfolio_1, 63, 0.99).VaR_kernel() #good 2nd check
fig = go.Figure()
fig.add_trace(go.Scatter(x=PnL_Portfolio_1.index, y=PnL_Portfolio_1['PnL_portfolio'], name='PnL Portfolio'))
fig.add_trace(go.Scatter(x=rolling_VaR_95.index, y=rolling_VaR_95.squeeze(), name='Rolling VaR 95%')) # Use squeeze() to convert DataFrame to Series if necessary
fig.add_trace(go.Scatter(x=rolling_VaR_99.index, y=rolling_VaR_99.squeeze(), name='Rolling VaR 99%')) # Use squeeze() to convert DataFrame to Series if necessary
fig.update_layout(title='Portfolio PnL and Rolling VaR', xaxis_title='Date', yaxis_title='Value')
fig.show()
The Non-Parametric Kernel VaR uses kernel density estimation (KDE) to approximate the probability distribution function of asset returns. KDE is a fundamental data smoothing problem where inferences about the population are made based on a finite data sample without assuming a parametric form for the distribution.
Kernel density estimation is formulated as:
$$ \hat{f}(x) = \frac{1}{n h} \sum_{i=1}^n K\left(\frac{x - X_i}{h}\right) $$Where:
The bandwidth can be chosen using rules like:
The VaR is determined by integrating the estimated density to find the cumulative distribution function (CDF), and then solving for the value at the desired confidence level $lpha$:
$$ \text{VaR}_{\alpha} = F^{-1}(1 - \alpha) $$Where $F^{-1}$ is the inverse of the estimated CDF.
The Kernel VaR method provides a flexible approach to estimating VaR by making minimal assumptions about the underlying statistical distribution of returns. This flexibility makes it especially useful in financial contexts where the return distributions may not follow standard parametric models and may exhibit properties like skewness, kurtosis, or multimodality. The choice of bandwidth and kernel can significantly affect the estimation, and these should be selected based on the context and characteristics of the data.